package scalab.tasks.compilers

import java.net.URL
import scala.tools.nsc.Settings
import scalab.tasks.DynamicTask

/**
* Dynamicaly loads a scala compiler and runs it.
*/

trait DynamicScalac extends DynamicTask with JVMCompiler{

	import scalab.core.component.Task
	import java.io.File
	import java.lang.reflect.Constructor

	override val binaryClassName: String = "scalab.tasks.compilers.ForeignScalac"

	val settings: Iterable[String]

	private def makeCompiler: Unit = {
		createClassLoader
		taskInstance = loadTaskClass.getConstructor(Array(classOf[String], classOf[Iterable[String]])).newInstance(Array(name, settings)).asInstanceOf[AnyRef]
	}

	private def compile(destinationDir: String): (Int, Int) = {//(errors, warnings)
		val result = invoke(
			"compile", 
			Array(classOf[String], classOf[List[String]]), 
			Array(destinationDir, srcs.sources.toList map {_.getPath})
		).asInstanceOf[Int]
		(result >> 16, result & 0x00FF)
	}

	def getCompiledFiles: Iterable[String] = invoke("getCompiledFiles", Array(), Array()).asInstanceOf[Iterable[String]]

	def execute(destinationDir: String): Boolean = try{

		makeCompiler
		
		val (errors, warnings) = compile(destinationDir)
		if(errors > 0){
			error("Compilation failed with " + errors + " error" + (if (errors > 1) "s" else "") + ".")
			false
		}
		else{
			built(getCompiledFiles)
			if (warnings > 0){
				warning("Compilation suceeded with " + warnings + " warning" + (if (warnings > 1) "s" else "") + ".")
			}
			else{
				info("Compilation Succeeded!")
			}
			true
		}	
	}
	catch{
		case t: Throwable =>
			error("DynamicScalac.execute: " + t.getMessage + "=> \n" + t.printStackTrace)
			false
	}
	finally{
		warning("free memory:" + Runtime.getRuntime.freeMemory)
		classLoader = null 
		taskInstance = null
		//unset classLoader  and taskInstance so that the compiler can be garbage collected
		//todo: add this feature in dynamictask => add listener after execution
		//even better: automatic graph dismantling
	}

	def instanceId: String = ""//take compilers arguments to compute this
}

object DynamicScalac{

	def apply(compilerName: String): DynamicScalac = apply(compilerName, Array())

	def apply(compilerName: String, options: Iterable[String]): DynamicScalac = new DynamicScalac{
			override val name: String = compilerName
			override val settings: Iterable[String] = options
			val bootClasspath = optionalIn
			val classpath = optionalIn
	}
}
